home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Graphics / Gnuplot / Source / help.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-02  |  18.3 KB  |  718 lines

  1. #ifndef lint
  2. static char *RCSid = "$Id: help.c%v 3.38.2.81 1993/02/24 02:29:34 woo Exp woo $";
  3. #endif
  4.  
  5.  
  6. /* GNUPLOT - help.c */
  7. /*
  8.  * Copyright (C) 1986 - 1993   Thomas Williams, Colin Kelley
  9.  *
  10.  * Permission to use, copy, and distribute this software and its
  11.  * documentation for any purpose with or without fee is hereby granted, 
  12.  * provided that the above copyright notice appear in all copies and 
  13.  * that both that copyright notice and this permission notice appear 
  14.  * in supporting documentation.
  15.  *
  16.  * Permission to modify the software is granted, but not the right to
  17.  * distribute the modified code.  Modifications are to be distributed 
  18.  * as patches to released version.
  19.  *  
  20.  * This software is provided "as is" without express or implied warranty.
  21.  * 
  22.  *
  23.  * AUTHORS
  24.  * 
  25.  *   Original Software:
  26.  *     Thomas Williams,  Colin Kelley.
  27.  * 
  28.  *   Gnuplot 2.0 additions:
  29.  *       Russell Lang, Dave Kotz, John Campbell.
  30.  *
  31.  *   Gnuplot 3.0 additions:
  32.  *       Gershon Elber and many others.
  33.  * 
  34.  * Send your comments or suggestions to 
  35.  *  info-gnuplot@dartmouth.edu.
  36.  * This is a mailing list; to join it send a note to 
  37.  *  info-gnuplot-request@dartmouth.edu.  
  38.  * Send bug reports to
  39.  *  bug-gnuplot@dartmouth.edu.
  40.  */
  41.  
  42. #include <stdio.h>
  43. #if defined (ATARI)
  44. #include "plot.h"
  45. #else
  46.  
  47. extern int strcmp();
  48. extern int strlen();
  49. extern char *strcpy();
  50. extern char *strncpy();
  51. extern char *strcat();
  52. extern char *strncat();
  53. extern char *getenv();
  54. extern FILE *fopen();
  55. extern char *malloc();
  56. #endif
  57.  
  58. extern int errno;
  59.  
  60. extern int instring();
  61.  
  62. #define    SAME    0    /* for strcmp() */
  63.  
  64. #include "help.h"    /* values passed back */
  65.  
  66. #if defined(__EMX__) || defined(DJGPP) || defined(DOS386)
  67. #ifdef MSDOS
  68. #undef MSDOS    /* we have plenty of memory under __EMX__ or DJGPP */
  69. #endif
  70. #ifdef unix
  71. #undef unix    /* we are not unix */
  72. #endif
  73. #endif
  74.  
  75. #ifdef OS2
  76.   /* GCC defines unix, but no PAGER, so... */
  77. #ifdef unix
  78. #undef unix
  79. #endif
  80. #endif  /* OS2 */
  81.  
  82. /* help -- help subsystem that understands defined keywords
  83. **
  84. ** Looks for the desired keyword in the help file at runtime, so you
  85. ** can give extra help or supply local customizations by merely editing
  86. ** the help file.
  87. **
  88. ** The original (single-file) idea and algorithm is by John D. Johnson,
  89. ** Hewlett-Packard Company.  Thanx and a tip of the Hatlo hat!
  90. **
  91. ** Much extension by David Kotz for use in gnutex, and then in gnuplot.
  92. ** Added output paging support, both unix and builtin. Rewrote completely
  93. ** to read helpfile into memory, avoiding reread of help file. 12/89.
  94. **
  95. ** Modified by Russell Lang to avoid reading completely into memory
  96. ** if MSDOS defined.  This uses much less memory.  6/91
  97. **
  98. ** The help file looks like this (the question marks are really in column 1):
  99. **
  100. **     ?topic
  101. **     This line is printed when the user wants help on "topic".
  102. **     ?keyword
  103. **     ?Keyword
  104. **     ?KEYWORD
  105. **     These lines will be printed on the screen if the user wanted
  106. **     help on "keyword", "Keyword", or "KEYWORD".  No casefolding is
  107. **    done on the keywords.
  108. **     ?subject
  109. **     ?alias
  110. **     This line is printed for help on "subject" and "alias".
  111. **     ?
  112. **    ??
  113. **     Since there is a null keyword for this line, this section
  114. **     is printed when the user wants general help (when a help
  115. **     keyword isn't given).  A command summary is usually here.
  116. **    Notice that the null keyword is equivalent to a "?" keyword
  117. **    here, because of the '?' and '??' topic lines above.
  118. **   If multiple keywords are given, the first is considered the 
  119. **   'primary' keyword. This affects a listing of available topics.
  120. **     ?last-subject
  121. **     Note that help sections are terminated by the start of the next
  122. **     '?' entry or by EOF.  So you can't have a leading '?' on a line
  123. **     of any help section.  You can re-define the magic character to
  124. **    recognize in column 1, though, if '?' is too useful.  (Try ^A.)
  125. */
  126.  
  127. #define    KEYFLAG    '?'    /* leading char in help file topic lines */
  128.  
  129. /*
  130. ** Calling sequence:
  131. **    int result;        # 0 == success
  132. **    char *keyword;        # topic to give help on
  133. **    char *pathname;        # path of help file
  134. **      int subtopics;        # set to TRUE if only subtopics to be listed
  135. **                # returns TRUE if subtopics were found
  136. **    result = help(keyword, pathname, &subtopics);
  137. ** Sample:
  138. **    cmd = "search\n";
  139. **    helpfile = "/usr/local/lib/program/program.help";
  140. **    subtopics = FALSE;
  141. **    if (help(cmd, helpfile, &subtopics) != H_FOUND)
  142. **        printf("Sorry, no help for %s", cmd);
  143. **
  144. **
  145. ** Speed this up by replacing the stdio calls with open/close/read/write.
  146. */
  147. #ifdef    WDLEN
  148. #  define    PATHSIZE    WDLEN
  149. #else
  150. #  define    PATHSIZE    BUFSIZ
  151. #endif
  152.  
  153. typedef int boolean;
  154. #ifndef TRUE
  155. #define TRUE (1)
  156. #define FALSE (0)
  157. #endif
  158.  
  159. typedef struct line_s LINEBUF;
  160. struct line_s {
  161.     char *line;            /* the text of this line */
  162.     LINEBUF *next;            /* the next line */
  163. };
  164.  
  165. typedef struct linkey_s LINKEY;
  166. struct linkey_s {
  167.     char *key;                /* the name of this key */
  168.     long pos;                /* ftell position */
  169.     LINEBUF *text;            /* the text for this key */
  170.     boolean primary;        /* TRUE -> is a primary name for a text block */
  171.     LINKEY *next;            /* the next key in linked list */
  172. };
  173.  
  174. typedef struct key_s KEY;
  175. struct key_s {
  176.     char *key;                /* the name of this key */
  177.     long pos;                /* ftell position */
  178.     LINEBUF *text;            /* the text for this key */
  179.     boolean primary;        /* TRUE -> is a primary name for a text block */
  180. };
  181. static LINKEY *keylist = NULL;    /* linked list of keys */
  182. static KEY *keys = NULL;        /* array of keys */
  183. static int keycount = 0;        /* number of keys */
  184. static FILE *helpfp = NULL;
  185.  
  186. static int LoadHelp();
  187. static void sortkeys();
  188. static int keycomp();
  189. static LINEBUF *storeline();
  190. static LINKEY *storekey();
  191. static KEY *FindHelp();
  192. static boolean Ambiguous();
  193.  
  194. /* Help output */
  195. static void PrintHelp();
  196. static void ShowSubtopics();
  197. static void StartOutput();
  198. static void OutLine();
  199. static void EndOutput();
  200. static FILE *outfile;        /* for unix pager, if any */
  201. static int pagelines;        /* count for builtin pager */
  202. #define SCREENSIZE 24        /* lines on screen (most have at least 24) */
  203.  
  204. /* help:
  205.  * print a help message 
  206.  * also print available subtopics, if subtopics is TRUE
  207.  */
  208. help(keyword, path, subtopics)
  209.     char *keyword;        /* on this topic */
  210.     char *path;            /* from this file */
  211.     boolean *subtopics;    /* (in) - subtopics only? */
  212.                         /* (out) - are there subtopics? */
  213. {
  214.     static char oldpath[PATHSIZE] = "";    /* previous help file */
  215.     int status;            /* result of LoadHelp */
  216.     KEY *key;            /* key that matches keyword */
  217.  
  218.     /*
  219.     ** Load the help file if necessary (say, first time we enter this routine,
  220.     ** or if the help file changes from the last time we were called).
  221.     ** Also may occur if in-memory copy was freed.
  222.     ** Calling routine may access errno to determine cause of H_ERROR.
  223.     */
  224.     errno = 0;
  225.     if (strncmp(oldpath, path, PATHSIZE) != SAME)
  226.      FreeHelp();
  227.     if (keys == NULL) {
  228.        status = LoadHelp(path);
  229.        if (status == H_ERROR)
  230.         return(status);
  231.  
  232.        /* save the new path in oldpath */
  233.        if (strlen(path) < PATHSIZE)
  234.         (void) strcpy(oldpath, path);
  235.        else {                /* not enough room in oldpath, sigh */
  236.           (void) strncpy(oldpath, path, PATHSIZE - 1);
  237.           oldpath[PATHSIZE - 1] = '\0';
  238.        }
  239.     }
  240.  
  241.     /* look for the keyword in the help file */
  242.     key = FindHelp(keyword);
  243.     if (key != NULL) {
  244.        /* found the keyword: print help and return */
  245.        PrintHelp(key, subtopics);
  246.        status = H_FOUND;
  247.     } else {
  248.        status = H_NOTFOUND;
  249.     }
  250.  
  251.     return(status);
  252. }
  253.  
  254. /* we only read the file once, into memory
  255.  * except for MSDOS when we don't read all the file -
  256.  * just the keys and location of the text
  257.  */
  258. static int
  259. LoadHelp(path)
  260.     char *path;
  261. {
  262.     LINKEY *key;            /* this key */
  263.     long pos;                /* ftell location within help file */
  264.     char buf[BUFSIZ];        /* line from help file */
  265.     LINEBUF *head;            /* head of text list  */
  266.     LINEBUF *firsthead = NULL;
  267.     boolean primary;        /* first ? line of a set is primary */
  268.     boolean flag;
  269.  
  270.     if ((helpfp = fopen(path, "r")) == NULL) {
  271.        /* can't open help file, so error exit */
  272.        return (H_ERROR);
  273.     }
  274.  
  275.     /*
  276.     ** The help file is open.  Look in there for the keyword.
  277.     */
  278.     (void) fgets(buf, BUFSIZ - 1, helpfp);
  279.     while (!feof(helpfp)) {
  280.        /*
  281.         ** Make an entry for each synonym keyword
  282.         */
  283.        primary = TRUE;
  284.        while (buf[0] == KEYFLAG) {
  285.           key = storekey(buf+1);    /* store this key */
  286.           key->primary = primary;
  287.           key->text = NULL;            /* fill in with real value later */
  288.           key->pos = 0;                /* fill in with real value later */
  289.           primary = FALSE;
  290.           pos = ftell(helpfp);
  291.           if (fgets(buf, BUFSIZ - 1, helpfp) == (char *)NULL)
  292.             break;
  293.        }
  294.        /*
  295.         ** Now store the text for this entry.
  296.         ** buf already contains the first line of text.
  297.         */
  298. #ifndef MSDOS
  299.        firsthead = storeline(buf);
  300.        head = firsthead;
  301. #endif
  302.        while ( (fgets(buf, BUFSIZ - 1, helpfp) != (char *)NULL)
  303.         && (buf[0] != KEYFLAG) ){
  304. #ifndef MSDOS
  305.           /* save text line */
  306.           head->next = storeline(buf);
  307.           head = head->next;
  308. #endif
  309.        }
  310.        /* make each synonym key point to the same text */
  311.        do {
  312.           key->pos = pos;
  313.           key->text = firsthead;
  314.           flag = key->primary;
  315.           key = key->next;
  316.        } while ( flag!=TRUE  &&  key!=NULL );
  317.     }
  318. #ifndef MSDOS
  319.     (void) fclose(helpfp);
  320. #endif
  321.  
  322.     /* we sort the keys so we can use binary search later */
  323.     sortkeys();
  324.     return(H_FOUND); /* ok */
  325. }
  326.  
  327. /* make a new line buffer and save this string there */
  328. static LINEBUF *
  329. storeline(text)
  330.     char *text;
  331. {
  332.     LINEBUF *new;
  333.  
  334.     new = (LINEBUF *)malloc(sizeof(LINEBUF));
  335.     if (new == NULL)
  336.      int_error("not enough memory to store help file", -1);
  337.     if (text != NULL) {
  338.        new->line = (char *) malloc((unsigned int)(strlen(text)+1));
  339.        if (new->line == NULL)
  340.         int_error("not enough memory to store help file", -1);
  341.        (void) strcpy(new->line, text);
  342.     } else
  343.      new->line = NULL;
  344.  
  345.     new->next = NULL;
  346.  
  347.     return(new);
  348. }
  349.  
  350. /* Add this keyword to the keys list, with the given text */
  351. static LINKEY *
  352. storekey(key)
  353.     char *key;
  354. {
  355.     LINKEY *new;
  356.  
  357.     key[strlen(key)-1] = '\0'; /* cut off \n  */
  358.  
  359.     new = (LINKEY *)malloc(sizeof(LINKEY));
  360.     if (new == NULL)
  361.      int_error("not enough memory to store help file", -1);
  362.     new->key = (char *) malloc((unsigned int)(strlen(key)+1));
  363.     if (new->key == NULL)
  364.      int_error("not enough memory to store help file", -1);
  365.     (void) strcpy(new->key, key);
  366.  
  367.     /* add to front of list */
  368.     new->next = keylist;
  369.     keylist = new;
  370.     keycount++;
  371.     return(new);
  372. }
  373.  
  374. /* we sort the keys so we can use binary search later */
  375. /* We have a linked list of keys and the number.
  376.  * to sort them we need an array, so we reform them into an array,
  377.  * and then throw away the list.
  378.  */
  379. static void
  380. sortkeys()
  381. {
  382.     LINKEY *p,*n;            /* pointers to linked list */
  383.     int i;                /* index into key array */
  384.     
  385.     /* allocate the array */
  386.     keys = (KEY *)malloc((unsigned int)((keycount+1) * sizeof(KEY)));
  387.     if (keys == NULL)
  388.      int_error("not enough memory to store help file", -1);
  389.     
  390.     /* copy info from list to array, freeing list */
  391.     for (p = keylist, i = 0; p != NULL; p = n, i++) {
  392.        keys[i].key = p->key;
  393.        keys[i].pos = p->pos;
  394.        keys[i].text = p->text;
  395.        keys[i].primary = p->primary;
  396.        n = p->next;
  397.        free( (char *)p );
  398.     }
  399.  
  400.     /* a null entry to terminate subtopic searches */
  401.     keys[keycount].key = NULL;
  402.     keys[keycount].pos = 0;
  403.     keys[keycount].text = NULL;
  404.  
  405.     /* sort the array */
  406.     /* note that it only moves objects of size (two pointers + long + int) */
  407.     /* it moves no strings */
  408.     qsort((char *)keys, keycount, sizeof(KEY), keycomp);
  409. }
  410.  
  411. static int
  412. keycomp(a, b)
  413.     KEY *a,*b;
  414. {
  415.     return (strcmp(a->key, b->key));
  416. }
  417.  
  418. /* Free the help file from memory. */
  419. /* May be called externally if space is needed */
  420. void
  421. FreeHelp()
  422. {
  423.     int i;                /* index into keys[] */
  424.     LINEBUF *t, *next;
  425.  
  426.     if (keys == NULL)
  427.      return;
  428.  
  429.     for (i = 0; i < keycount; i++) {
  430.        free( (char *)keys[i].key );
  431.        if (keys[i].primary)   /* only try to release text once! */
  432.        for (t = keys[i].text; t != NULL; t = next) {
  433.           free( (char *)t->line );
  434.           next = t->next;
  435.           free( (char *)t );
  436.        }
  437.     }
  438.     free( (char *)keys );
  439.     keys = NULL;
  440.     keycount = 0;
  441. #ifdef MSDOS
  442.     (void) fclose(helpfp);
  443. #endif
  444. }
  445.  
  446. /* FindHelp:
  447.  *  Find the key that matches the keyword.
  448.  *  The keys[] array is sorted by key.
  449.  *  We could use a binary search, but a linear search will aid our
  450.  *  attempt to allow abbreviations. We search for the first thing that
  451.  *  matches all the text we're given. If not an exact match, then
  452.  *  it is an abbreviated match, and there must be no other abbreviated
  453.  *  matches -- for if there are, the abbreviation is ambiguous. 
  454.  *  We print the ambiguous matches in that case, and return not found.
  455.  */
  456. static KEY *                /* NULL if not found */
  457. FindHelp(keyword)
  458.     char *keyword;            /* string we look for */
  459. {
  460.     KEY *key;
  461.     int len = strlen(keyword);
  462.     int compare;
  463.  
  464.     for (key = keys, compare = 1; key->key != NULL && compare > 0; key++) {
  465.        compare = strncmp(keyword, key->key, len);
  466.        if (compare == 0)    /* we have a match! */
  467.         if (!Ambiguous(key, len)) {
  468.             /* non-ambiguous abbreviation */
  469.             (void) strcpy(keyword, key->key); /* give back the full spelling */
  470.             return(key);        /* found!! */
  471.         }
  472.     }
  473.  
  474.     /* not found, or ambiguous */
  475.     return(NULL);
  476. }
  477.  
  478. /* Ambiguous:
  479.  * Check the key for ambiguity up to the given length.
  480.  * It is ambiguous if it is not a complete string and there are other
  481.  * keys following it with the same leading substring.
  482.  */
  483. static boolean
  484. Ambiguous(key, len)
  485.     KEY *key;
  486.     int len;
  487. {
  488.     char *first;
  489.     char *prev;
  490.     boolean status = FALSE;    /* assume not ambiguous */
  491.     int compare;
  492.     int sublen;
  493.  
  494.     if (key->key[len] == '\0')
  495.      return(FALSE);
  496.     
  497.     for (prev = first = key->key, compare = 0, key++;
  498.         key->key != NULL && compare == 0; key++) {
  499.        compare = strncmp(first, key->key, len);
  500.        if (compare == 0) {
  501.           /* So this key matches the first one, up to len.
  502.            * But is it different enough from the previous one
  503.            * to bother printing it as a separate choice?
  504.            */
  505.           sublen = instring(prev+len, ' ');
  506.           if (strncmp(key->key, prev, len+sublen) != 0) {
  507.              /* yup, this is different up to the next space */
  508.              if (!status) {
  509.                 /* first one we have printed is special */
  510.                 fprintf(stderr, 
  511.                        "Ambiguous request '%.*s'; possible matches:\n",
  512.                        len, first);
  513.                 fprintf(stderr, "\t%s\n", prev);
  514.                 status = TRUE;
  515.              }
  516.              fprintf(stderr, "\t%s\n", key->key);
  517.              prev = key->key;
  518.           }
  519.        }
  520.     }
  521.     
  522.     return(status);
  523. }
  524.  
  525. /* PrintHelp:
  526.  * print the text for key
  527.  */
  528. static void
  529. PrintHelp(key, subtopics)
  530.     KEY *key;
  531.     boolean *subtopics;        /* (in) - subtopics only? */
  532.                         /* (out) - are there subtopics? */
  533. {
  534.     LINEBUF *t;
  535. #ifdef MSDOS
  536.     char buf[BUFSIZ];        /* line from help file */
  537. #endif
  538.  
  539.     StartOutput();
  540.  
  541.     if (subtopics == NULL || !*subtopics) {
  542. #ifdef MSDOS
  543.        fseek(helpfp,key->pos,0);
  544.        while ( (fgets(buf, BUFSIZ - 1, helpfp) != (char *)NULL)
  545.             && (buf[0] != KEYFLAG) ) {
  546.           OutLine(buf);
  547.        }
  548. #else
  549.        for (t = key->text; t != NULL; t = t->next)
  550.         OutLine(t->line);        /* print text line */
  551. #endif
  552.     }
  553.  
  554.     ShowSubtopics(key, subtopics);
  555.     OutLine("\n");
  556.  
  557.     EndOutput();
  558. }
  559.  
  560. /* ShowSubtopics:
  561.  *  Print a list of subtopic names
  562.  */
  563. #define PER_LINE 4
  564.  
  565. static void
  566. ShowSubtopics(key, subtopics)
  567.     KEY *key;                /* the topic */
  568.     boolean *subtopics;        /* (out) are there any subtopics */
  569. {
  570.     int subt = 0;            /* printed any subtopics yet? */
  571.     KEY *subkey;            /* subtopic key */
  572.     int len;                /* length of key name */
  573.     char line[BUFSIZ];        /* subtopic output line */
  574.     char *start;            /* position of subname in key name */
  575.     int sublen;            /* length of subname */
  576.     int pos;
  577.     char *prev = NULL;        /* the last thing we put on the list */
  578.  
  579.     *line = '\0';
  580.     len = strlen(key->key);
  581.  
  582.     for (subkey = key+1; subkey->key != NULL; subkey++) {
  583.        if (strncmp(subkey->key, key->key, len) == 0) {
  584.           /* find this subtopic name */
  585.           start = subkey->key + len;
  586.           if (len > 0)
  587.             if (*start == ' ')
  588.              start++;        /* skip space */
  589.             else
  590.              break;        /* not the same topic after all  */
  591.           else            /* here we are looking for main topics */
  592.             if (!subkey->primary)
  593.              continue;    /* not a main topic */
  594.           sublen = instring(start, ' ');
  595.           if (prev == NULL || strncmp(start, prev, sublen) != 0) {
  596.              if (subt == 0) {
  597.                 subt++;
  598.                 if (len)
  599.                   (void) sprintf(line, "\nSubtopics available for %s:\n", 
  600.                         key->key);
  601.                 else
  602.                   (void) sprintf(line, "\nHelp topics available:\n");
  603.                 OutLine(line);
  604.                 *line = '\0';
  605.                 pos = 0;
  606.              }
  607.              if (pos == PER_LINE) {
  608.                 (void) strcat(line, "\n");
  609.                 OutLine(line);
  610.                 *line = '\0';
  611.                 pos = 0;
  612.              }
  613.              /* adapted by DvdSchaaf */
  614.              {
  615. #define FIRSTCOL    6
  616. #define COLLENGTH    15
  617.                 int spacelen, ispacelen;
  618.  
  619.                  if( pos == 0 )
  620.                     spacelen = FIRSTCOL;
  621.                  for( ispacelen = 0;
  622.                     ispacelen < spacelen; ispacelen++ )
  623.                     (void) strcat(line, " ");
  624.                  /* commented out *
  625.                  (void) strcat(line, "\t");
  626.                  */
  627.                  (void) strncat(line, start, sublen);
  628.                  spacelen = COLLENGTH - sublen;
  629.                  if( spacelen <= 0 )
  630.                     spacelen = 1;
  631.              }
  632.              pos++;
  633.              prev = start;
  634.           }
  635.        } else {
  636.           /* new topic */
  637.           break;
  638.        }
  639.     }
  640.     
  641.     /* put out the last line */
  642.     if (subt > 0 && pos > 0) {
  643.        (void) strcat(line, "\n");
  644.        OutLine(line);
  645.     }
  646.     
  647. /*
  648.     if (subt == 0) {
  649.        OutLine("\n");
  650.        OutLine("No subtopics available\n");
  651.     }
  652. */
  653.     
  654.     if (subtopics)
  655.      *subtopics = (subt != 0);
  656. }
  657.  
  658.  
  659. /* StartOutput:
  660.  * Open a file pointer to a pipe to user's $PAGER, if there is one,
  661.  * otherwise use our own pager.
  662.  */
  663. static void
  664. StartOutput()
  665. {
  666. #ifdef unix
  667.     char *pager_name = getenv("PAGER");
  668.     extern FILE *popen();
  669.  
  670.     if (pager_name != NULL && *pager_name != '\0')
  671.      if ((outfile = popen(pager_name, "w")) != (FILE *)NULL)
  672.        return;            /* success */
  673.     outfile = stderr;
  674.     /* fall through to built-in pager */
  675. #endif
  676.  
  677.     /* built-in pager */
  678.     pagelines = 0;
  679. }
  680.  
  681. /* write a line of help output  */
  682. /* line should contain only one \n, at the end */
  683. static void
  684. OutLine(line)
  685.     char *line;
  686. {
  687.     int c;                /* dummy input char */
  688. #ifdef unix
  689.     if (outfile != stderr) {
  690.        fputs(line, outfile);
  691.        return;
  692.     }
  693. #endif
  694.  
  695.     /* built-in dumb pager */
  696.     /* leave room for prompt line */
  697.     if (pagelines >= SCREENSIZE - 2) {
  698.        fprintf(stderr,"Press return for more: ");
  699.        do 
  700.         c = getchar();
  701.        while (c != EOF && c != '\n');
  702.        pagelines = 0;
  703.     }
  704.     fputs(line, stderr);
  705.     pagelines++;
  706. }
  707.  
  708. static void
  709. EndOutput()
  710. {
  711. #ifdef unix
  712.     extern int pclose();
  713.  
  714.     if (outfile != stderr)
  715.      (void) pclose(outfile);
  716. #endif
  717. }
  718.